﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Threading;
using PersonaLibrary.Converters;
using PersonaLibrary.Data;
using PersonaLibrary.PrintReports;
using PersonaLibrary.PrintViewModel.Model;
using PersonaLibrary.View.PrintView;

namespace PersonaLibrary.ViewModel.PrintViewModel
{
	sealed internal class BithdayTemplate : BasePrintTemplate, Model.ITemplateConfigurable, Model.IAvailableConflict, Model.IOrder
	{
		private readonly BithdayProperty _property = new BithdayProperty();
		private readonly ContactsConflictVM _conflicts = new ContactsConflictVM();
		private OrderViewModel _orderVm;

		private readonly List<StackPanel> _panels;

		private RelayCommand _config;
		private RelayCommand _conflict;
		private RelayCommand _order;

		private static Regex _eyarRegex = new Regex(@"\d{4}$", RegexOptions.Compiled);
		private Dictionary<Employee, Grid> _wrappedItems;

		public BithdayTemplate()
		{
			using(CustomPropertiesContextDataContext pContext = new CustomPropertiesContextDataContext())
			{
				CustomProperty cProperty = pContext.CustomProperties.FirstOrDefault(x => x.Name == GetType().Name);
				if (cProperty != null)
				{
					IFormatter formatter = new BinaryFormatter();
					try
					{
						_property.Property = (BithdayProperty.SerializeProperty)formatter.Deserialize(new MemoryStream(cProperty.Data.ToArray()));
					}
					catch(Exception)
					{
						
					}
				}
			}

			if (_property.Columns == null)
				CreateDefaultColumns();
			else
				ValidateColumns(_property.Property.Columns);

			SetColumnsSelector();

			_panels = new List<StackPanel>();

			_property.ProperiesApply += PropertyProperiesApply;

		}

		private void ValidateColumns(List<Column> columns)
		{
			Column updatecolumn = columns.FirstOrDefault(x => x.Header == "Сведения на дату");
			if (updatecolumn == null)
			{
				columns.Insert(1, new Column("Сведения на дату") {Width = new GridLength(0.9, GridUnitType.Star)});
				PropertyProperiesApply();
			}
		}

		void PropertyProperiesApply()
		{
			using (CustomPropertiesContextDataContext pContext = new CustomPropertiesContextDataContext())
			{
				CustomProperty cProperty = pContext.CustomProperties.FirstOrDefault(x => x.Name == GetType().Name);
				if (cProperty == null)
				{
					cProperty = new CustomProperty { Name = GetType().Name };
					pContext.CustomProperties.InsertOnSubmit(cProperty);
				}

				IFormatter formatter = new BinaryFormatter();
				MemoryStream stream = new MemoryStream();
				formatter.Serialize(stream, _property.Property);
				cProperty.Data = stream.ToArray();
				stream.Close();
				pContext.SubmitChanges();
			}
			Refresh();
		}

		public RelayCommand ShowOrder
		{
			get
			{
				//if(_order == null)
				//    _order = new RelayCommand(x => _orderVm.View.Show(), x => true);
				return _order;
			}
		}

		public RelayCommand ShowConflict
		{
			get
			{
				//if (_conflict == null)
				//    _conflict = new RelayCommand(x => _conflicts.View.Show(), x => canShowConflict());
				return _conflict;
			}
		}

		public RelayCommand ShowConfig
		{
			get
			{
				//if(_config == null)
				//    _config = new RelayCommand(x => _property.View.Show(), x => !_property.IsWindowOpened);
				return _config;
			}
		}

		private bool canShowConflict()
		{
			//if (_conflicts.IsWindowOpened)
			//    return false;

			return true;
		}

		protected override IDocumentPaginatorSource CreateTable(Size pageSize, Thickness thickness)
		{
			if (_orderVm == null)
			{
				_orderVm = new OrderViewModel(Items);
				_orderVm.Submitted += OrderVmSubmitted;
			}

			Stopwatch timer = Stopwatch.StartNew();

			if (_conflicts.AllConflictEmployee == null)
				_conflicts.AllConflictEmployee = new List<Employee>(Items.Where(item => item.IsConflict));

			timer.Stop();
			Debug.WriteLine("Create conflicts " + timer.Elapsed);
			
			FixedDocument result = new FixedDocument();
			result.DocumentPaginator.PageSize = pageSize;
			
			int index = 0;
			double contentWidht = pageSize.Width - thickness.Left - thickness.Right;
			StackPanel host = new StackPanel{Margin = thickness, Width = contentWidht};
			PageContent page;

			Size pageSizePositive = new Size(result.DocumentPaginator.PageSize.Width, double.PositiveInfinity);

			if(!string.IsNullOrEmpty(_property.ReportHeader))
			{
				TextBlock reportHeader = new TextBlock
				                         	{
				                         		Text = _property.ReportHeader,
				                         		FontSize = 16,
				                         		HorizontalAlignment = HorizontalAlignment.Center,
				                         		TextAlignment = TextAlignment.Center,
				                         		Margin = new Thickness(0, 0, 0, 15)
				                         	};

				reportHeader.Measure(pageSizePositive);
				host.Children.Add(reportHeader);
			}

			Grid headerGrid = CreateHeader();
			headerGrid.Measure(pageSizePositive);
			host.Children.Add(headerGrid);

			if (_wrappedItems == null)
			{
				Stopwatch createTimer = Stopwatch.StartNew();
				CreateControls();
				createTimer.Stop();
				Debug.WriteLine(string.Format("Create controls {0}", createTimer.Elapsed));
			}

			if (_panels != null)
			{
				foreach (StackPanel panel in _panels)
				{
					panel.Children.Clear();
				}

				_panels.Clear();
			}

			List<Grid> grids = _wrappedItems.Values.ToList();
			
			while (index < grids.Count)
			{
				Grid grid = grids[index];

				grid.Width = contentWidht;

				grid.Measure(pageSizePositive);
				host.Children.Add(grid);
				
				host.Measure(pageSizePositive);

				_panels.Add(host);

				if (host.DesiredSize.Height > result.DocumentPaginator.PageSize.Height && host.Children.Count > 1)
				{
					host.Children.Remove(grid);
					page = new PageContent();
					FixedPage fPage = new FixedPage();
					fPage.Children.Add(host);
					((IAddChild)page).AddChild(fPage);
					result.Pages.Add(page);
					host = new StackPanel { Margin = thickness, Width = contentWidht };

					if (!string.IsNullOrEmpty(_property.ReportHeader) && _property.ReportHeaderInAll)
					{
						TextBlock reportHeader = new TextBlock
						{
							Text = _property.ReportHeader,
							FontSize = 16,
							HorizontalAlignment = HorizontalAlignment.Center,
							TextAlignment = TextAlignment.Center,
							Margin = new Thickness(0, 0, 0, 15)
						};

						reportHeader.Measure(pageSizePositive);
						host.Children.Add(reportHeader);
					}

					if(_property.TableHeaderInAll)
					{
						headerGrid = CreateHeader();

						headerGrid.Measure(pageSizePositive);
						host.Children.Add(headerGrid);
					}
				}
				else
					index++;
			}

			page = new PageContent();
			FixedPage fixPage = new FixedPage();
			fixPage.Children.Add(host);
			((IAddChild)page).AddChild(fixPage);
			result.Pages.Add(page);
			
			return result;
		}

		void OrderVmSubmitted()
		{
			Sort();
		}

		private void Sort()
		{
			Dictionary<Employee,Grid> wrappedItems = new Dictionary<Employee, Grid>(_wrappedItems.Count);

			foreach (Employee employee in _orderVm.Employees)
			{
				wrappedItems.Add(_wrappedItems.First(x => x.Key.Id == employee.Id).Key, _wrappedItems.First(x => x.Key.Id == employee.Id).Value);
			}

			Items = wrappedItems.Keys.ToList();
			CreateControls();
		}

		private void CreateControls()
		{
			_wrappedItems = new Dictionary<Employee, Grid>(Items.Count);

			int currentAge = DateTime.Now.Year;

			Stopwatch sqlSelect = Stopwatch.StartNew();

			LinqToDbDataContext context = new LinqToDbDataContext {ObjectTrackingEnabled = false};
			List<int> employeeIds = Items.Select(x => x.Id).ToList();
			var jobs = context.ConnectionJobEmplDeps.Where(x => employeeIds.Contains(x.EmployeeId)).Select(x => new
			                                                                                                    	{
			                                                                                                    		employeeId = x.EmployeeId,
																														jobName = x.Job.Name,
																														OrganizationName = x.Department.Organization.ToString()
			                                                                                                    	}).ToList();
			context.Dispose();
			sqlSelect.Stop();
			Debug.WriteLine( "Sql select time" + sqlSelect.Elapsed);

			foreach (Employee employee in Items)
			{
				Grid grid = new Grid();

				ReportItemText text = new ReportItemText(_property.Columns[0]);
				FrameworkElement control = text.ToControl(employee, (Items.IndexOf(employee) + 1).ToString());
				CreateControl(grid, control, _property.Columns[0]);

				ReportItemText text1 = new ReportItemText(_property.Columns[1]);
				string value = "-";
				if (employee.UpdateDate.HasValue)
					value = employee.UpdateDate.Value.ToShortDateString();
				FrameworkElement control1 = text1.ToControl(employee, value);
				CreateControl(grid, control1, _property.Columns[1]);

				ReportItemText text2 = new ReportItemText(_property.Columns[2]);
				FrameworkElement control2 = text2.ToControl(employee, employee.ToString());
				CreateControl(grid, control2, _property.Columns[2]);

				ReportItemText text3 = new ReportItemText(_property.Columns[3]);
				FrameworkElement control3 = text3.ToControl(employee, employee.MultiBirthday);
				CreateControl(grid, control3, _property.Columns[3]);

				string ageValue = "";
				string matchValue = "";
				if(!string.IsNullOrEmpty(employee.MultiBirthday))
					matchValue = _eyarRegex.Match(employee.MultiBirthday).Value;
				int parseResult;
				if(int.TryParse(matchValue, out parseResult))
					ageValue = (currentAge - parseResult).ToString();

				ReportItemText text4 = new ReportItemText(_property.Columns[4]);
				FrameworkElement control4 = text4.ToControl(employee, ageValue);
				CreateControl(grid, control4, _property.Columns[4]);

				StringBuilder result = new StringBuilder();
				foreach (var conn in jobs.Where(x => x.employeeId == employee.Id))
					result.AppendLine(conn.OrganizationName);

				ReportItemText text5 = new ReportItemText(_property.Columns[5]);
				FrameworkElement control5 = text5.ToControl(employee, result.ToString());
				CreateControl(grid, control5, _property.Columns[5]);

				List<string> jobResult = new List<string>();
				foreach (var conn in jobs.Where(x => x.employeeId == employee.Id))
					if(!jobResult.Contains(conn.jobName))
						jobResult.Add(conn.jobName);

				ReportItemText text6 = new ReportItemText(_property.Columns[6]);
				FrameworkElement control6 = text6.ToControl(employee, string.Join(Environment.NewLine, jobResult));
				CreateControl(grid, control6, _property.Columns[6]);

				ReportItemText text7 = new ReportItemText(_property.Columns[7]);
				FrameworkElement control7 = text7.ToControl(employee);
				CreateControl(grid, control7, _property.Columns[7]);
				
				ReportItemText text8 = new ReportItemText(_property.Columns[8]);
				FrameworkElement control8 = text8.ToControl(employee);
				CreateControl(grid, control8, _property.Columns[8]);

				ReportItemText text9 = new ReportItemText(_property.Columns[9]);
				FrameworkElement control9 = text9.ToControl(employee);
				CreateControl(grid, control9, _property.Columns[9]);
				
				_wrappedItems.Add(employee, grid);
			}
		}

		private static void CreateControl(Grid grid, FrameworkElement control, Column column)
		{
			ColumnWidthConverter columnWidthConverter = new ColumnWidthConverter();
			ColumnDefinition columnDefinition = new ColumnDefinition();
			Binding widthBinding = new Binding("IsVisible")
			{
				Converter = columnWidthConverter,
				Source = column,
				ConverterParameter = column
			};

			columnDefinition.SetBinding(ColumnDefinition.WidthProperty, widthBinding);
			grid.ColumnDefinitions.Add(columnDefinition);

			Border border = column.CellBorder;
			border.Child = control;

			Binding visibleBinding = new Binding("IsVisible")
			                         	{
			                         		Converter = new CollapsedConverter(),
			                         		Source = column,
			                         	};

			border.SetBinding(Border.VisibilityProperty, visibleBinding);

			grid.Children.Add(border);


			Grid.SetColumn(border, grid.ColumnDefinitions.Count - 1);
		}

		private Grid CreateHeader()
		{
			Grid headerGrid = new Grid();

			foreach (Column column in _property.Columns)
			{
				ColumnDefinition columnDefinition = new ColumnDefinition();

				Binding widthBinding = new Binding("IsVisible")
				                       	{
				                       		Converter = new ColumnWidthConverter(),
				                       		Source = column,
				                       		ConverterParameter = column
				                       	};

				columnDefinition.SetBinding(ColumnDefinition.WidthProperty, widthBinding);

				headerGrid.ColumnDefinitions.Add(columnDefinition);
				
				ReportItemText text = new ReportItemText(column) { Text = column.Header };
				TextBox control = text.ToControl(null) as TextBox;
				control.VerticalAlignment = VerticalAlignment.Center;
				control.HorizontalAlignment = HorizontalAlignment.Center;
				control.FontWeight = FontWeights.Bold;
				control.AcceptsReturn = true;
				Border border = column.CellBorder;
				border.Child = control;

				Binding visibleBinding = new Binding("IsVisible")
				{
					Converter = new CollapsedConverter(),
					Source = column,
				};

				border.SetBinding(Border.VisibilityProperty, visibleBinding);

				headerGrid.Children.Add(border);
				Grid.SetColumn(border, headerGrid.ColumnDefinitions.Count - 1);
			}
			return headerGrid;
		}

		private void CreateDefaultColumns()
		{
			_property.Columns = new List<Column>
			                    	{
			                    		new Column("N")
			                    			{Width = new GridLength(0.5, GridUnitType.Star)},
										new Column("Сведения на дату")
			                    			{Width = new GridLength(0.9, GridUnitType.Star)},
			                    		new Column("ФИО")
			                    			{Width = new GridLength(2, GridUnitType.Star)},
			                    		new Column("Дата рождения")
			                    			{
			                    				Width = new GridLength(0.9, GridUnitType.Star)
			                    			},
			                    		new Column("Возраст")
			                    			{
			                    				Width = new GridLength(0.7, GridUnitType.Star)
			                    			},
			                    		new Column("Организация")
			                    			{
			                    				Width = new GridLength(1.5, GridUnitType.Star)
			                    			},
			                    		new Column("Должность")
			                    			{
			                    				Width = new GridLength(1.5, GridUnitType.Star)
			                    			},
			                    		new Column("Открытка/Факс/Звонок/Подарок") {Width = new GridLength(1.5, GridUnitType.Star)},
			                    		new Column("Адрес")
			                    			{
			                    				Width = new GridLength(1.5, GridUnitType.Star)
			                    			},
			                    		new Column("Факс")
			                    			{
			                    				Width = new GridLength(1.5, GridUnitType.Star)
			                    			},
			                    	};
		}

		private void SetColumnsSelector()
		{
			_property.Columns[8].ValueSelector = delegate(Employee arg)
			                                     	{
			                                     		string value = arg.DefaultAddress;
			                                     		return value;
			                                     	};
			_property.Columns[9].ValueSelector = delegate(Employee arg)
			                                     	{
			                                     		return arg.DefaultPhone;
			                                     	};
		}

		public override void UnLoad()
		{
			_property.View.Close();
		}

		
	}
}
